using JigLibX.Collision;
using JigLibX.Geometry;
using JigLibX.Physics;
using Microsoft.Xna.Framework;

namespace Innovation
{
    // Provides a base object type for physics simulation
    public abstract class PhysicsObject : Component, I3DComponent
    {
        // Local copy of the mass of the object
        float mass = 1;

        // The Body managed by the PhysicsObject
        public Body Body;

        // The CollisionSkin managed by the PhysicsObject
        public CollisionSkin CollisionSkin;

        // The mass of the PhysicsObject
        public float Mass
        {
            get { return mass; }
            set { 
                // Set the new value
                mass = value;

                // Fix transforms
                Vector3 com = SetMass(value); 
                if (CollisionSkin != null) 
                    CollisionSkin.ApplyLocalTransform(
                        new JigLibX.Math.Transform(-com, Matrix.Identity));
            }
        }

        Transform transform = new Transform();
        public Transform Transform { get { return transform; } }

        public PhysicsObject()
        {
            Transform.PositionChanged += new System.EventHandler(Transform_PositionChanged);
            Transform.RotationChanged += new System.EventHandler(Transform_RotationChanged);
        }

        void Transform_RotationChanged(object sender, System.EventArgs e)
        {
            Body.MoveTo(Body.Position, Transform.Rotation);
        }

        void Transform_PositionChanged(object sender, System.EventArgs e)
        {
            Body.MoveTo(Transform.Position, Body.Orientation);
        }

        // Whether or not the physics object is locked in place
        public bool Immovable
        {
            get { return Body.Immovable; }
            set { Body.Immovable = value; }
        }

        // Returns the PhysicsObject's BoundingBox
        public BoundingBox BoundingBox 
        { 
            get 
            { 
                if (Body.CollisionSkin != null) 
                    return Body.CollisionSkin.WorldBoundingBox; 
                else 
                    return new BoundingBox(Transform.Position - Vector3.One, 
                        Transform.Position + Vector3.One); 
            } 
        }

        // The body's velocity
        public Vector3 Velocity
        {
            get { return Body.Velocity; }
            set { Body.Velocity = value; }
        }

        // Sets up the body and collision skin
        protected void InitializeBody()
        {
            Body = new Body();
            CollisionSkin = new CollisionSkin(Body);
            Body.CollisionSkin = this.CollisionSkin;
            Body.EnableBody();
        }

        public override void Update()
        {
            transform = new Transform(Body.Position, Body.Orientation, transform.Scale);
        }

        // Sets the mass of the PhysicsObject
        public Vector3 SetMass(float mass)
        {
            PrimitiveProperties primitiveProperties =
                new PrimitiveProperties(
                    PrimitiveProperties.MassDistributionEnum.Solid, 
                    PrimitiveProperties.MassTypeEnum.Density, mass);

            float junk; Vector3 com; Matrix it, itCoM;

            CollisionSkin.GetMassProperties(primitiveProperties, 
                out junk, out com, out it, out itCoM);
            Body.BodyInertia = itCoM;
            Body.Mass = junk;

            return com;
        }
       
        // Rotates and moves the model relative to the physics object to 
        // better align the model with the object
        public void OffsetModel(Vector3 PositionOffset, 
            Matrix RotationOffset)
        {
            CollisionSkin.ApplyLocalTransform(
                new JigLibX.Math.Transform(PositionOffset, RotationOffset));
        }

        // Disables physics body and component
        public override void DisableComponent()
        {
            Body.DisableBody();
            base.DisableComponent();
        }
    }
}
